package com.gbits.app.finance.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections.CollectionUtils;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.util.StringUtils;

import com.gbits.app.finance.api.Page;

public class BaseDaoImpl4Hibernate4<T> implements IBaseDao<T> {
	
	private SessionFactory sessionFactory;
	
	protected Class<T> entityclass;
	
	public BaseDaoImpl4Hibernate4() {
		super();
		this.entityclass = getEntityClass();
	}

	public BaseDaoImpl4Hibernate4(SessionFactory sessionFactory,
			Class<T> entityclass) {
		super();
		this.sessionFactory = sessionFactory;
		this.entityclass = entityclass;
	}

	public BaseDaoImpl4Hibernate4(SessionFactory sessionFactory) {
		super();
		this.sessionFactory = sessionFactory;
		this.entityclass = getEntityClass();
	}

	/**
	 * @return the sessionFactory
	 */
	protected SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	/* (non-Javadoc)
	 * @see com.gbits.websns.kernel.common.dao.IBaseDao#create(java.lang.Object)
	 */
	public T create(T entity) {
		sessionFactory.getCurrentSession().save(entity);
		this.flush();
		return entity;
	}

	/* (non-Javadoc)
	 * @see com.gbits.websns.kernel.common.dao.IBaseDao#edit(java.lang.Object)
	 */
	public T update(T entity) {
		sessionFactory.getCurrentSession().update(entity);
		this.flush();
		return entity;
	}

	/* (non-Javadoc)
	 * @see com.gbits.websns.kernel.common.dao.IBaseDao#remove(java.lang.Object)
	 */
	public void remove(T entity) {
		sessionFactory.getCurrentSession().delete(entity);
		this.flush();
	}

	/* (non-Javadoc)
	 * @see com.gbits.websns.kernel.common.dao.IBaseDao#delete(java.util.List)
	 */
	public void delete(List<T> objs) {
		for (T o : objs) {
			sessionFactory.getCurrentSession().delete(o);
		}
		this.flush();
	}

	/* (non-Javadoc)
	 * @see com.gbits.websns.kernel.common.dao.IBaseDao#get(java.io.Serializable)
	 */
	@SuppressWarnings("unchecked")
	public T get(Serializable id) {
		return (T)sessionFactory.getCurrentSession().get(entityclass, id);
	}

	/* (non-Javadoc)
	 * @see com.gbits.websns.kernel.common.dao.IBaseDao#load(java.io.Serializable)
	 */
	@SuppressWarnings("unchecked")
	public T load(Serializable id) {
		return (T)sessionFactory.getCurrentSession().load(entityclass, id);
	}

	/* (non-Javadoc)
	 * @see com.gbits.websns.kernel.common.dao.IBaseDao#load(java.lang.Object, java.io.Serializable)
	 */
	public void load(T entityObject, Serializable id) {
		sessionFactory.getCurrentSession().load(entityObject, id);
	}

	/* (non-Javadoc)
	 * @see com.gbits.websns.kernel.common.dao.IBaseDao#findAll()
	 */
	@SuppressWarnings("unchecked")
	public List<T> findAll() {
		return sessionFactory.getCurrentSession().createCriteria(entityclass).list();
	}

	/* (non-Javadoc)
	 * @see com.gbits.websns.kernel.common.dao.IBaseDao#getListByIds(java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	public List<T> getListByIds(String ids) {
		if(StringUtils.isEmpty(ids)){
			return new ArrayList<T>();
		}
		return sessionFactory.getCurrentSession().createCriteria(entityclass).add(Restrictions.in("id", ids.split(","))).list();
	}

	/* (non-Javadoc)
	 * @see com.gbits.websns.kernel.common.dao.IBaseDao#find(java.lang.String, java.lang.Object[])
	 */
	@SuppressWarnings("unchecked")
	public List<T> find(String hql, Object... values) {
		Query queryObject = sessionFactory.getCurrentSession().createQuery(hql);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				Object value = values[i];
				queryObject.setParameter(i, values[i]);
			}
		}
		return queryObject.list();
	}
	
	public List<T> findByNamesParameter(String hql, Map<String, Object> map) {
		Query queryObject = sessionFactory.getCurrentSession().createQuery(hql);
		if (map != null) {
			for (String name : map.keySet()) {
				Object value = map.get(name);
				if (value instanceof Collection) {
					queryObject.setParameterList(name, (Collection)value);
					continue;
				}
				queryObject.setParameter(name, value);
			}
		}
		return queryObject.list();
	}

	/* (non-Javadoc)
	 * @see com.gbits.websns.kernel.common.dao.IBaseDao#get(java.lang.String, java.lang.Object[])
	 */
	public T get(String hql, Object... values) {
		List<T> list = this.find(hql, values);
		if(CollectionUtils.isNotEmpty(list)){
			return list.get(0);
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see com.gbits.websns.kernel.common.dao.IBaseDao#getListForPage(java.lang.String, int, int)
	 */
	@SuppressWarnings("unchecked")
	public List<T> getListForPage(String hql, int offset, int length) {
		return sessionFactory.getCurrentSession().createQuery(hql).setFirstResult(offset).setMaxResults(length).list();
	}

	/* (non-Javadoc)
	 * @see com.gbits.websns.kernel.common.dao.IBaseDao#flush()
	 */
	public void flush() {
		sessionFactory.getCurrentSession().flush();
	}

	/* (non-Javadoc)
	 * @see com.gbits.websns.kernel.common.dao.IBaseDao#clear()
	 */
	public void clear() {
		sessionFactory.getCurrentSession().clear();
	}

	/* (non-Javadoc)
	 * @see com.gbits.websns.kernel.common.dao.IBaseDao#refresh(java.lang.Object)
	 */
	public void refresh(T entityObject) {
		sessionFactory.getCurrentSession().refresh(entityObject);
	}

	/* (non-Javadoc)
	 * @see com.gbits.websns.kernel.common.dao.IBaseDao#evict(java.lang.Object)
	 */
	public void evict(T entityObject) {
		sessionFactory.getCurrentSession().evict(entityObject);
	}

	/* (non-Javadoc)
	 * @see com.gbits.websns.kernel.common.dao.IBaseDao#toLike(java.lang.String)
	 */
	public String toLike(String value) {
		if (null == value || "".equals(value))
		{
			return "%";
		}
		return "%" + value + "%";
	}	
	
	@SuppressWarnings("unchecked")
	public List<T> query(final String queryString, final Map<Object, Object> parameters){
		Query query = sessionFactory.getCurrentSession().createQuery(queryString);
		if(null != parameters)
		{
			for(Object key : parameters.keySet())
			{
				query.setParameter((String)key, parameters.get(key));
			}
		}
		return query.list();
	}
	
	@SuppressWarnings("unchecked")
	public List<T> queryForPage(final String queryString, final Map<Object, Object> parameters, int from, int size){
		Query query = sessionFactory.getCurrentSession().createQuery(queryString);
		if(null != parameters)
		{
			for(Object key : parameters.keySet())
			{
				query.setParameter((String)key, parameters.get(key));
			}
		}
		return query.setFirstResult(from).setMaxResults(size).list();		
	}
	
	@SuppressWarnings("unchecked")
	protected Class<T> getEntityClass() {
		Type type = getClass().getGenericSuperclass();
		Class<T> result = null;
		if (type instanceof ParameterizedType) {
			ParameterizedType parameterizedType = (ParameterizedType) type;
			result = (Class<T>) parameterizedType.getActualTypeArguments()[0];
		}
		return result;
	}

	public <T> Page<T> queryByPage(Class<T> entityClass,
			Criterion[] criterions, Order[] orders, Page<T> page) {
		final Long currentPage = page.getCurrentPage();
		final Long pageSize = page.getPageSize();

		final Long startIndex = Page.getStart(currentPage, pageSize);
		Session session = this.sessionFactory.getCurrentSession();
		Criteria c = session.createCriteria(entityClass);
		if (criterions != null)
		{
			for (int i = 0; i < criterions.length; i++)
			{
				c.add(criterions[i]);
			}
		}

		Long cnt = (Long) c.setProjection(Projections.rowCount())
				.uniqueResult();
		// 注意:取totalCount的语句必须在以下代码的前面,否则查询不到记录为空null
		c.setProjection(null);

		page.setTotal(cnt);

		if (pageSize > 0)
		{
			c.setFirstResult(startIndex.intValue());
			c.setMaxResults(pageSize.intValue());
		}

		if (null != orders)
		{
			for (Order o : orders)
			{
				c.addOrder(o);
			}
		}

		if (page.getTotal() > 0) {
			// 设置只返回对像 不返回数组
			@SuppressWarnings("unchecked")
			List<T> list = c
					.setResultTransformer(CriteriaSpecification.ROOT_ENTITY).list();
			page.setData(list);
		} else {
			page.setData(Collections.EMPTY_LIST);
		}
		
		
		return page;
	}

	public <T> Page<T> queryByPage(String queryString,
			Map<String, Object> parameters, Page<T> page) {
		final Long currentPage = page.getCurrentPage();
		final Long pageSize = page.getPageSize();

		final Long startIndex = Page.getStart(currentPage, pageSize);
		Session session = sessionFactory.getCurrentSession();
		Query query = session.createQuery(queryString);
		
		if(null != parameters)
		{
			Set<String> keys = parameters.keySet();
			for(String key : keys)
			{
				query.setParameter(key, parameters.get(key));
			}
		}
		
		// 设置总大小
		ScrollableResults scrollResult = query.scroll();
		if(scrollResult.last() && scrollResult.getRowNumber() >= 0)
		{
			page.setTotal(scrollResult.getRowNumber() + 1);
		}
		else
		{
			page.setTotal(0);
		}
		if (page.getTotal() > 0) {
			@SuppressWarnings("rawtypes")
			List list = query.setFirstResult(startIndex.intValue())
			 	 .setMaxResults(pageSize.intValue())
			 	 .list();
			page.setData(list);
		} else {
			page.setData(Collections.EMPTY_LIST);
		}
		
		return page;
	}
	
	/**
	 * 执行更新操作
	 * @param hql
	 * @param parameters
	 * @return
	 */
	public int executeUpdate(String hql, Map<String, Object> parameters) {
		Session session = this.sessionFactory.getCurrentSession();
		Query updatedQuery = session.createQuery(hql);
		if (null != parameters) {
			for (String key : parameters.keySet()) {
				updatedQuery.setParameter(key, parameters.get(key));
			}
		}
		return updatedQuery.executeUpdate();
	}
	
}
